<?php

namespace common\core\component;

use http\Url;
use yii\base\Component;

class Iapppay extends Component
{
    public $appId;

    public $appkey;

    public $platpkey;

    //爱贝商户后台接入url
// $coolyunCpUrl="http://pay.coolyun.com:6988";
    protected $iapppayCpUrl = "http://ipay.iapppay.com:9999";
//登录令牌认证接口 url
    protected $tokenCheckUrl = "http://ipay.iapppay.com:9999/openid/openidcheck";

//下单接口 url
// $orderUrl=$coolyunCpUrl . "/payapi/order";
    // protected $orderUrl = "http://ipay.iapppay.com:9999/payapi/order";
    protected $orderUrl = "https://cp.iapppay.com/payapi/order";

//支付结果查询接口 url
    protected $queryResultUrl = "http://ipay.iapppay.com:9999/payapi/queryresult";

//契约查询接口url
    protected $querysubsUrl = "http://ipay.iapppay.com:9999/payapi/subsquery";

//契约鉴权接口Url
    protected $ContractAuthenticationUrl = "http://ipay.iapppay.com:9999/payapi/subsauth";

//取消契约接口Url
    protected $subcancel = "http://ipay.iapppay.com:9999/payapi/subcancel";
//H5和PC跳转版支付接口Url
    protected $h5url = "https://web.iapppay.com/h5/gateway?";
    protected $pcurl = "https://web.iapppay.com/pc/gateway?";


    /**
     * @throws \Exception
     */
    public function init()
    {
        parent::init();
        //数据现组装成：{"appid":"12313","logintoken":"aewrasera98seuta98e"}
//        $contentdata["appid"]=$this->appId;
//        $contentdata["logintoken"]="55e37ac2c0dc98972475d640";//这个需要调登录接口时时获取。有效期10min
//        //组装请求报文 格式：$reqData="transdata={"appid":"123","logintoken":"3213213"}&sign=xxxxxx&signtype=RSA"
//        $reqData = $this->composeReq($contentdata, $this->appkey);
//        echo "reqData:$reqData\n";
//        $this->HttpPost($this->tokenCheckUrl,$reqData);

    }

    public function create_order($data)
    {
        $transid = '';
        //下单接口
        $orderReq['appid'] = $this->appId;
        $orderReq['waresid'] = 7;//intval($data['goods_type']);
        $orderReq['cporderid'] = $data['order_no']; //确保该参数每次 都不一样。否则下单会出问题。
        $orderReq['price'] = floatval($data['goods_price']);   //单位：元
        $orderReq['currency'] = 'RMB';
        $orderReq['appuserid'] = $data['user_id'];
        //$orderReq['cpprivateinfo'] = 'success';//支付完成后发送支付结果通知时会透传给商户
        $orderReq['notifyurl'] = \Yii::$app->params['api_url'].'/trading_result';//商户服务端接收支付结果通知的地址
        //组装请求报文  对数据签名
       // return json_encode($orderReq);
        $reqData = $this->composeReq($orderReq, $this->appkey);
        //echo  "$reqData";
        //发送到爱贝服务后台请求下单
        //print_r($reqData);exit();

        $respData = $this->request_by_curl($this->orderUrl, $reqData, 'MPL test');
        //echo "respData:$respData\n";
        //验签数据并且解析返回报文
        if (!$this->parseResp($respData, $this->platpkey, $respJson)) {
            return $transid;
        } else {
            $transid = $respJson->transid;
            return $transid;
        }
    }

    public function gateWay($transid){
        $data['tid'] = $transid;
        $data['app'] = $this->appId;
        $data['url_r'] = $transid;
        $data['url_h'] = $transid;
    }
//此为H5 和PC 版本调收银台时需要的参数组装函数   特别提醒的是  下面的函数中有  $h5url 和$pcurl 两个url地址。 只需要更换这两个地址就可以 调出 H5 收银台和PC版本收银台。
    public function H5orPCpay($transid) {
        $h5url = 'https://web.iapppay.com/pay/gateway?';
        $orderReq['tid'] = $transid;

        $orderReq['app'] = $this->appId; // appid

        $orderReq['url_r'] = \Yii::$app->params['front_end']."/paymentResult"; //支付成功后支付回调URL地址

        $orderReq['url_h'] = \Yii::$app->params['front_end'].'/paymentResult'; //用户取消返回商户URL地址
        //组装请求报文   对数据签名
        //return $orderReq;
        $reqData = $this->h5composeReq($orderReq, $this->appkey);
        //return $reqData;
        return $h5url.$reqData;
        echo "参数组装完成：请用浏览器访问该链接:$h5url$reqData\n";//这里组装的最终数据 就可以用浏览器访问调出收银台。
        echo "<script language=\"javascript\">";
        echo "location.href=\"$h5url$reqData\"";//我们的常连接版本 有PC 版本 和移动版本。 根据使用的环境不同请更换相应的URL:$h5url,$pcurl.
        echo "</script>";
    }
    public function tradingResult($string)
    {
        //$string = $_POST;//接收post请求数据

        if ($string == null) {
            return false;
        } else {
            $transdata = $string['transdata'];
            //echo "$transdata\n";
            if (stripos("%22", $transdata)) { //判断接收到的数据是否做过 Urldecode处理，如果没有处理则对数据进行Urldecode处理
                $string = array_map('urldecode', $string);
            }
            $respData = 'transdata=' . $string['transdata'] . '&sign=' . $string['sign'] . '&signtype=' . $string['signtype'];//把数据组装成验签函数要求的参数格式
            //  验签函数parseResp（） 中 只接受明文数据。数据如：transdata={"appid":"3003686553","appuserid":"10123059","cporderid":"1234qwedfq2as123sdf3f1231234r","cpprivate":"11qwe123r23q232111","currency":"RMB","feetype":0,"money":0.12,"paytype":403,"result":0,"transid":"32011601231456558678","transtime":"2016-01-23 14:57:15","transtype":0,"waresid":1}&sign=jeSp7L6GtZaO/KiP5XSA4vvq5yxBpq4PFqXyEoktkPqkE5b8jS7aeHlgV5zDLIeyqfVJKKuypNUdrpMLbSQhC8G4pDwdpTs/GTbDw/stxFXBGgrt9zugWRcpL56k9XEXM5ao95fTu9PO8jMNfIV9mMMyTRLT3lCAJGrKL17xXv4=&signtype=RSA
            //file_put_contents('./log.txt', $respData,FILE_APPEND);
            //echo "进入了2" . $respData;
            if (!$this->parseResp($respData, $this->platpkey, $respJson)) {
                //验签失败
                return false;
            } else {
                //验签成功
               // echo 'success' . "\n";
                //以下是 验签通过之后 对数据的解析。
                $transdata = $string['transdata'];
                $arr = json_decode($transdata);
                //return $arr;
                $res_data['appid'] = $arr->appid;
                $res_data['appuserid'] = $arr->appuserid;
                $res_data['cporderid'] = $arr->cporderid;
                $res_data['cpprivate'] = $arr->cpprivate;
                $res_data['money'] = $arr->money;
                $res_data['result'] = $arr->result;
                $res_data['transid'] = $arr->transid;
                $res_data['transtime'] = $arr->transtime;
                $res_data['waresid'] = $arr->waresid;
                return $res_data;
            }
        }

    }


    /**格式化公钥
     * $pubKey PKCS#1格式的公钥串
     * return pem格式公钥， 可以保存为.pem文件
     */
    public function formatPubKey($pubKey)
    {
        $fKey = "-----BEGIN PUBLIC KEY-----\n";
        $len = strlen($pubKey);
        for ($i = 0; $i < $len;) {
            $fKey = $fKey . substr($pubKey, $i, 64) . "\n";
            $i += 64;
        }
        $fKey .= "-----END PUBLIC KEY-----";
        return $fKey;
    }


    /**格式化公钥
     * $priKey PKCS#1格式的私钥串
     * return pem格式私钥， 可以保存为.pem文件
     */
    public function formatPriKey($priKey)
    {
        $fKey = "-----BEGIN RSA PRIVATE KEY-----\n";
        $len = strlen($priKey);
        for ($i = 0; $i < $len;) {
            $fKey = $fKey . substr($priKey, $i, 64) . "\n";
            $i += 64;
        }
        $fKey .= "-----END RSA PRIVATE KEY-----";
        return $fKey;
    }

    /**RSA签名
     * $data待签名数据
     * $priKey商户私钥
     * 签名用商户私钥
     * 使用MD5摘要算法
     * 最后的签名，需要用base64编码
     * return Sign签名
     */
    public function sign($data, $priKey)
    {
        //转换为openssl密钥
        $res = openssl_get_privatekey($priKey);

        //调用openssl内置签名方法，生成签名$sign
        openssl_sign($data, $sign, $res, OPENSSL_ALGO_MD5);

        //释放资源
        openssl_free_key($res);

        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    /**RSA验签
     * $data待签名数据
     * $sign需要验签的签名
     * $pubKey爱贝公钥
     * 验签用爱贝公钥，摘要算法为MD5
     * return 验签是否通过 bool值
     */
    public function verify($data, $sign, $pubKey)
    {
        //转换为openssl格式密钥
        $res = openssl_get_publickey($pubKey);

        //调用openssl内置方法验签，返回bool值
        $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_MD5);

        //释放资源
        openssl_free_key($res);

        //返回资源是否成功
        return $result;
    }


    /**
     * 解析response报文
     * $content  收到的response报文
     * $pkey     爱贝平台公钥，用于验签
     * $respJson 返回解析后的json报文
     * return    解析成功TRUE，失败FALSE
     */
    public function parseResp($content, $pkey, &$respJson)
    {
        $arr = array_map(create_function('$v', 'return explode("=", $v);'), explode('&', $content));
        foreach ($arr as $value) {
            $resp[($value[0])] = $value[1];
        }

        //解析transdata
        if (array_key_exists("transdata", $resp)) {
            $respJson = json_decode($resp["transdata"]);
        } else {
            return FALSE;
        }

        //验证签名，失败应答报文没有sign，跳过验签
        if (array_key_exists("sign", $resp)) {
            //校验签名
            $pkey = $this->formatPubKey($pkey);
            return verify($resp["transdata"], $resp["sign"], $pkey);
        } else if (array_key_exists("errmsg", $respJson)) {
            return FALSE;
        }

        return TRUE;
    }

    /**
     * curl方式发送post报文
     * $remoteServer 请求地址
     * $postData post报文内容
     * $userAgent用户属性
     * return 返回报文
     */
    public function request_by_curl($remoteServer, $postData, $userAgent)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $remoteServer);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
        $data = urldecode(curl_exec($ch));
        curl_close($ch);

        return $data;
    }


    /**
     * 组装request报文
     * $reqJson 需要组装的json报文
     * $vkey  cp私钥，格式化之前的私钥
     * return 返回组装后的报文
     */
    public function composeReq($reqJson, $vkey)
    {
        //获取待签名字符串
        $content = json_encode($reqJson);
        //格式化key，建议将格式化后的key保存，直接调用
        $vkey = $this->formatPriKey($vkey);

        //生成签名
        $sign = $this->sign($content, $vkey);
        //组装请求报文，目前签名方式只支持RSA这一种
        $reqData = "transdata=" . urlencode($content) . "&sign=" . urlencode($sign) . "&signtype=RSA";

        return $reqData;
    }

//H5请求报文拼接
    public function h5composeReq($reqJson, $vkey)
    {
        //获取待签名字符串
        $content = json_encode($reqJson);
        //格式化key，建议将格式化后的key保存，直接调用
        $vkey = $this->formatPriKey($vkey);

        //生成签名
        $sign = $this->sign($content, $vkey);

        //组装请求报文，目前签名方式只支持RSA这一种
        $reqData = "data=" . urlencode($content) . "&sign=" . urlencode($sign) . "&sign_type=RSA";

        return $reqData;
    }


    //发送post请求 ，并得到响应数据  和对数据进行验签
    public function HttpPost($Url, $reqData)
    {
        $respData = $this->request_by_curl($Url, $reqData, " demo ");
        if (!$this->parseResp($respData, $this->platpkey, $notifyJson)) {
            echo "fail";
        }
        echo "TEST respData:$respData\n";
    }

}